package mwt.xml.xdbforms.xformlayer.transactions.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import mwt.xml.xdbforms.dblayer.ConnectionFactory;
import mwt.xml.xdbforms.dblayer.query.Criteria;
import mwt.xml.xdbforms.dblayer.query.Criterion;
import mwt.xml.xdbforms.dblayer.query.Query;
import mwt.xml.xdbforms.dblayer.query.SQLOperation;
import mwt.xml.xdbforms.dblayer.query.exception.QueryException;
import mwt.xml.xdbforms.dblayer.query.impl.CriteriaImpl;
import mwt.xml.xdbforms.dblayer.query.impl.Restriction;
import mwt.xml.xdbforms.dblayer.query.impl.SQLInsert;
import mwt.xml.xdbforms.dblayer.query.impl.SQLParameter;
import mwt.xml.xdbforms.dblayer.query.impl.SQLUpdate;
import mwt.xml.xdbforms.schemalayer.SchemaDocument;
import mwt.xml.xdbforms.schemalayer.SchemaTypeMapping;
import mwt.xml.xdbforms.schemalayer.exception.SchemaBuildingException;
import mwt.xml.xdbforms.schemalayer.exception.SchemaQueryException;
import mwt.xml.xdbforms.schemalayer.impl.SchemaTypeMappingHandler;
import mwt.xml.xdbforms.xformlayer.transactions.CommitterService;
import mwt.xml.xdbforms.xformlayer.transactions.exception.CommitterServiceCommitEx;
import mwt.xml.xdbforms.xformlayer.transactions.exception.CommitterServiceValidateEx;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Progetto Master Web Technology
 * @author Gianfranco Murador, Cristian Castiglia, Matteo Ferri
 * @copyright (C) 2009, MCG08
 */
public class CommitterServiceImpl implements CommitterService {

    private InputStream inputSource;
    private SchemaDocument sd;
    private SchemaTypeMapping typeMapping;
    private String sdoc;

    public CommitterServiceImpl() {
        typeMapping = new SchemaTypeMappingHandler();
    }

    public void validate(SchemaDocument sd, InputStream inputSource) throws CommitterServiceValidateEx {

        ValidatorErrorHandler veh = new ValidatorErrorHandler();
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        schemaFactory.setErrorHandler(veh);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int r;
        try {
            while ((r = inputSource.read()) != -1) {
                bos.write(r);
            }
        } catch (IOException ex) {
            Logger.getLogger(CommitterServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        byte[] data = bos.toByteArray();
        sdoc = new String(data);
        this.inputSource = inputSource;
        this.sd = sd;

        /*
        try {
            DOMSource schemaSource = new DOMSource(sd.getSchemaDocument());
        // Schema s = schemaFactory.newSchema(schemaSource);
        //Validator v = s.newValidator();
        //v.setErrorHandler(veh);
            /*v.validate(new StreamSource(inputSource));

        if (veh.hadProblems()) {

        throw new CommitterServiceValidateEx("Data submitting not valid");
        }*/

        /*} catch (IOException ex) {
        throw new CommitterServiceValidateEx(ex.getMessage(), ex);
        /*    } catch (SAXException ex) {
        throw new CommitterServiceValidateEx(ex.getMessage(), ex);
        } catch (SchemaBuildingException ex) {
            throw new CommitterServiceValidateEx(ex.getMessage(), ex);
        }*/

    }

    public void insert(String table) throws CommitterServiceCommitEx {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        dbf.setValidating(false);


        try {



            DocumentBuilder builder = dbf.newDocumentBuilder();
            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(sdoc));
            Document d = builder.parse(is);

            String xpathQuery = "//data/*";
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xp = xpf.newXPath();
            XPathExpression expression = xp.compile(xpathQuery);
            String[] columnType = sd.getAllColumnsType();
            NodeList nl = (NodeList) expression.evaluate(d, XPathConstants.NODESET);

            Object[] sqlParams = new Object[nl.getLength()];

            SQLOperation sqlo = new SQLInsert(table);

            for (int i = 0; i < nl.getLength(); i++) {

                Node n = nl.item(i);
                String colname = n.getNodeName();
                String value = n.getTextContent();

                if (sd.getAutoincrementTypeName(colname) == null) {

                    sqlParams[i] = typeMapping.getXSchemaToJavaType(columnType[i], value);
                    sqlo.declareParameter(new SQLParameter(colname), sqlParams[i]);
                }

            }

            ConnectionFactory cf = ConnectionFactory.getInstance();
            Query query = cf.getQuery();
            query.insert(sqlo);

        } catch (QueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SchemaQueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (XPathExpressionException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (ParserConfigurationException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        }

    }

    public void update(String table, Object[] keys) throws CommitterServiceCommitEx {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        dbf.setValidating(false);
        Document d;

        try {
            DocumentBuilder builder = dbf.newDocumentBuilder();

            System.out.println("DATI RITORNATI " + sdoc + " END");

            InputSource is = new InputSource();
            is.setCharacterStream(new StringReader(sdoc));
            d = builder.parse(is);

            String xpathQuery = "//data/*";
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xp = xpf.newXPath();
            XPathExpression expression = xp.compile(xpathQuery);
            String[] columnType = sd.getAllColumnsType();
            NodeList nl = (NodeList) expression.evaluate(d, XPathConstants.NODESET);

            Object[] sqlParams = new Object[nl.getLength()];

            SQLOperation operation = new SQLUpdate(table);

            for (int i = 0; i < nl.getLength(); i++) {

                Node n = nl.item(i);
                String colname = n.getNodeName();

                System.out.println("Colname " + colname);

                String value = n.getTextContent();

                System.out.println("ColumnType " + columnType[i] + " value " + value);


                sqlParams[i] = typeMapping.getXSchemaToJavaType(columnType[i], value);
                operation.declareParameter(new SQLParameter(colname), sqlParams[i]);
            }

            Criteria criteria = new CriteriaImpl();

            String pkColname[] = sd.getAllPrimaryKeysNames();

            if (pkColname.length != keys.length) {

                throw new CommitterServiceCommitEx("Error in the keys");

            }

            /*

            Criterion criterions[] = new Criterion[pkColname.length];

            for (int i = 0; i < keys.length; i++) {
            Object object = keys[i];
            criterions[i] = Restriction.eq(pkColname[i], object);

            }

            Criterion and = Restriction.and(criterions[0], criterions[1]);
            for (int i = 2; i < criterions.length; i++) {

            and = Restriction.and(and, criterions[i]);
            }

            criteria.addCriterion(and);
             *
             */

            Criterion criterions[] = new Criterion[pkColname.length];


            if (criterions.length == 1) {
                criterions[0] = Restriction.eq(pkColname[0], keys[0]);
                criteria.addCriterion(criterions[0]);
            } else {


                for (int i = 0; i < keys.length; i++) {
                    Object object = keys[i];
                    System.out.print("----------");
                    System.out.println((Integer) object);
                    criterions[i] = Restriction.eq(pkColname[i], object);

                }
                Criterion and = Restriction.and(criterions[0], criterions[1]);

                if (criterions.length > 2) {

                    for (int i = 2; i < criterions.length; i++) {

                        and = Restriction.and(and, criterions[i]);
                    }
                }
                criteria.addCriterion(and);

            }

            operation.addCriteria(criteria);

            System.out.println("Operation : " + operation.toString());

            ConnectionFactory cf = ConnectionFactory.getInstance();
            Query query = cf.getQuery();
            query.update(operation);



        } catch (QueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SchemaQueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (XPathExpressionException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (ParserConfigurationException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        }


    }

    public String getInsertStmt(String table) throws CommitterServiceCommitEx {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        dbf.setValidating(false);


        try {
            DocumentBuilder builder = dbf.newDocumentBuilder();
            Document d = builder.parse(inputSource);
            String xpathQuery = "//data/*";
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xp = xpf.newXPath();
            XPathExpression expression = xp.compile(xpathQuery);
            String[] columnType = sd.getAllColumnsType();
            NodeList nl = (NodeList) expression.evaluate(d, XPathConstants.NODESET);

            Object[] sqlParams = new Object[nl.getLength()];



            SQLOperation sqlo = new SQLInsert(table);

            for (int i = 0; i < nl.getLength(); i++) {

                Node n = nl.item(i);
                String colname = n.getNodeName();
                String value = n.getNodeValue();
                sqlParams[i] = typeMapping.getXSchemaToJavaType(columnType[i], value);
                sqlo.declareParameter(new SQLParameter(colname), sqlParams[i]);

            }

            ConnectionFactory cf = ConnectionFactory.getInstance();
            Query query = cf.getQuery();
            return query.getInsertStmt(sqlo);

        } catch (QueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SchemaQueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (XPathExpressionException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (ParserConfigurationException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        }



    }

    public String getUpdateStmt(String table) throws CommitterServiceCommitEx {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(false);
        dbf.setValidating(false);
        List<Object> lkeys = new ArrayList<Object>();

        try {
            DocumentBuilder builder = dbf.newDocumentBuilder();
            Document d = builder.parse(inputSource);
            String xpathQuery = "//data/*";
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xp = xpf.newXPath();
            XPathExpression expression = xp.compile(xpathQuery);
            String[] columnType = sd.getAllColumnsType();
            NodeList nl = (NodeList) expression.evaluate(d, XPathConstants.NODESET);

            Object[] sqlParams = new Object[nl.getLength()];
            SQLOperation operation = new SQLUpdate(table);
            String pkColname[] = sd.getAllPrimaryKeysNames();

            for (int i = 0; i < nl.getLength(); i++) {

                Node n = nl.item(i);
                String colname = n.getNodeName();
                String value = n.getNodeValue();
                sqlParams[i] = typeMapping.getXSchemaToJavaType(columnType[i], value);
                operation.declareParameter(new SQLParameter(colname), sqlParams[i]);

                for (int j = 0; j < pkColname.length; j++) {
                    String pkname = pkColname[j];
                    if (colname.equals(pkname)) {
                        lkeys.add(value);
                    }
                }
            }

            Object keys[] = lkeys.toArray();
            Criteria criteria = new CriteriaImpl();

            /*
            Criterion criterions[] = new Criterion[pkColname.length];

            for (int i = 0; i < keys.length; i++) {
            Object object = keys[i];
            criterions[i] = Restriction.eq(pkColname[i], object);

            }

            Criterion and = Restriction.and(criterions[0], criterions[1]);
            for (int i = 2; i < criterions.length; i++) {

            and = Restriction.and(and, criterions[i]);
            }

            criteria.addCriterion(and);
             */

            Criterion criterions[] = new Criterion[pkColname.length];


            if (criterions.length == 1) {
                criterions[0] = Restriction.eq(pkColname[0], keys[0]);
                criteria.addCriterion(criterions[0]);
            } else {


                for (int i = 0; i < keys.length; i++) {
                    Object object = keys[i];
                    System.out.print("----------");
                    System.out.println((Integer) object);
                    criterions[i] = Restriction.eq(pkColname[i], object);

                }
                Criterion and = Restriction.and(criterions[0], criterions[1]);

                if (criterions.length > 2) {

                    for (int i = 2; i < criterions.length; i++) {

                        and = Restriction.and(and, criterions[i]);
                    }
                }
                criteria.addCriterion(and);

            }
            operation.addCriteria(criteria);

            ConnectionFactory cf = ConnectionFactory.getInstance();
            Query query = cf.getQuery();
            return query.getUpdateStmt(operation);


        } catch (QueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SchemaQueryException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (XPathExpressionException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (SAXException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        } catch (ParserConfigurationException ex) {
            throw new CommitterServiceCommitEx(ex.getMessage(), ex);
        }

    }
}
